home *** CD-ROM | disk | FTP | other *** search
- /*
- * devHBA.c --
- *
- * This is a prototype template descibing the Sprite interface to SCSI
- * (Small Computer System Interface) HBAs (Host Bus Adapters). This file
- * only descibes the interface from the machine independent part of the
- * device module to the HBA. This template assumes that the HBA is of
- * the sort that you send a command to the device and are interrupted
- * when the command completes.
- *
- * Copyright 1988 Regents of the University of California
- * Permission to use, copy, modify, and distribute this
- * software and its documentation for any purpose and without
- * fee is hereby granted, provided that the above copyright
- * notice appear in all copies. The University of California
- * makes no representations about the suitability of this
- * software for any purpose. It is provided "as is" without
- * express or implied warranty.
- */
-
- #ifndef lint
- static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/dev/devHBA.c.proto,v 9.0 89/09/12 14:58:20 douglis Stable $ SPRITE (Berkeley)";
- #endif not lint
-
- #include "sprite.h"
- #include "scsi<HBATYPE>.h"
- #include "mach.h"
- #include "dev.h"
- #include "devInt.h"
- #include "scsiHBA.h"
- #include "scsiDevice.h"
- #include "sync.h"
- #include "stdlib.h"
-
- /*
- * Controller - The Data structure describing a <HBATYPE> controller. One
- * of these structures exists for each active <HBATYPE> on the system. Each
- * controller may have from zero to 56 (7 targets each with 8 logical units)
- * devices attached to it.
- */
- typedef Controller struct Controller {
- CtrlRegs *regsPtr; /* Pointer to the registers of this controller. */
- char *name; /* String for error message for this controller. */
- DevCtrlQueues devQueues; /* Device queues for devices attached to this
- * controller. */
- Sync_Semaphore mutex; /* Lock protecting controller's data structures. */
- /* Until disconnect/reconnect is added we can have
- * only one current active device and scsi command.*/
- Device *devPtr; /* Current active command. */
- ScsiCmd *scsiCmdPtr; /* Current active command. */
- Device *devicePtr[8][8]; /* Pointers to the device attached to the
- * controller index by [targetID][LUN].
- * NIL if device not attached yet. Zero if
- * device conflicts with HBA address. */
-
- } Controller;
-
- /*
- * MAX_CTRLS - Maximum number of <HBATYPE> controllers attached to the
- * system. We set this to the maximum number of VME slots
- * in any system currently available to us
- */
- #define MAX_CTRLS 16
- static Controller *Controllers[MAX_CTRLS];
-
- /*
- * Device - The data structure containing information about a device. One of
- * these structure is kept for each attached device. Note that is structure
- * is casted into a ScsiDevice and returned to higher level software.
- * This implies that the ScsiDevice must be the first field in this
- * structure.
- */
-
- typedef struct Device {
- ScsiDevice handle; /* Scsi Device handle. This is the only part
- * of this structure visible to higher
- * level software.
- * MUST BE FIRST FIELD IN STRUCTURE. */
- int targetID; /* SCSI Target ID of this device. Note that
- * the LUN is store in the device handle. */
- Controller *ctrlPtr; /* Controller to which device is attached. */
- /*
- * The following part of this structure is
- * used to handle SCSI commands that return
- * CHECK status. To handle the REQUEST SENSE
- * command we must: 1) Save the state of the current
- * command into the "struct FrozenCommand". 2) Submit
- * a request sense command formatted in SenseCmd
- * to the device. */
- struct FrozenCommand {
- ScsiCmd *scsiCmdPtr; /* The frozen command. */
- unsigned char statusByte; /* It's SCSI status byte, Will always have
- * the check bit set. */
- int amountTransferred; /* Number of bytes transferred by this
- * command. */
- } frozen;
- char senseBuffer[DEV_MAX_SENSE_BYTES]; /* Data buffer for request sense */
- ScsiCmd SenseCmd; /* Request sense command buffer. */
- } Device;
-
-
- /*
- *----------------------------------------------------------------------
- *
- * Dev<HBATYPE>Init --
- *
- * This routine is called from the machine dependent part of the device
- * module at system boot time to probe for and initialize the HBA.
- *
- * Results:
- * TRUE if the HBA exists, FALSE otherwise.
- *
- * Side effects:
- * Memory may be allocated.
- *
- *----------------------------------------------------------------------
- */
- Dev<HBATYPE>Init(ctrlLocPtr)
- DevConfigController *ctrlLocPtr; /* Controller location. */
- {
- int ctrlNum;
- Boolean found;
- Controller *ctrlPtr;
- int i,j;
-
- /*
- * See if the controller is there.
- */
- ctrlNum = ctrlLocPtr->controllerID;
- found = ProbeForHBA(ctrlLocPtr->address);
- if (!found) {
- return FALSE;
- }
- /*
- * It's there. Allocate and fill in the Controller structure.
- */
- Controllers[ctrlNum] = ctrlPtr = (Controller *) malloc(sizeof(Controller));
- bzero((char *) ctrlPtr, sizeof(Controller));
- ctrlPtr->regsPtr = (CtrlRegs *) (ctrlLocPtr->address);
- ctrlPtr->name = ctrlLocPtr->name;
- Sync_SemInitDynamic(&(ctrlPtr->mutex),ctrlPtr->name);
- /*
- * Initialized the name, device queue header, and the master lock.
- * The controller comes up with no devices active and no devices
- * attached. Reserved the devices associated with the
- * targetID of the controller (7).
- */
- /*
- * Allocate the device queue header for the devices attached to this
- * HBA. Note we share our MASTER_LOCK() with the DevQueue code to
- * much simply synronization. We are notified of incomming requests by
- * the DevQueue module calls to entryAvailProc.
- */
- ctrlPtr->devQueues = Dev_CtrlQueuesCreate(&(ctrlPtr->mutex),entryAvailProc);
- for (i = 0; i < 8; i++) {
- for (j = 0; j < 8; j++) {
- ctrlPtr->devicePtr[i][j] = (i == 7) ? (Device *) 0 : (Device *) NIL;
- }
- }
- ctrlPtr->scsiCmdPtr = (ScsiCmd *) NIL;
- Controllers[ctrlNum] = ctrlPtr;
- Reset(ctrlPtr);
- return TRUE;
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * Dev<HBATYPE>AttachDevice --
- *
- * Attach a SCSI device using the Sun SCSI3 HBA.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- ScsiDevice *
- Dev<HBATYPE>AttachDevice(devicePtr, insertProc)
- Fs_Device *devicePtr; /* Device to attach. */
- void (*insertProc)(); /* Queue insert procedure. */
- {
- Device *devPtr;
- Controller *ctrlPtr;
- char tmpBuffer[512];
- int length;
- int ctrlNum;
- int targetID, lun;
-
- /*
- * The Fs_Device structure tells the attach procedure the HBA controller
- * number (only useful on machine that can have more than on controller
- * of a certain type) and the SCSI TargetID and LUN of the device.
- * This code here assumes that the machine depended part of the device
- * module has probed for and filled in the Controllers array.
- */
- ctrlNum = SCSI_HBA_NUMBER(devicePtr);
- if ((ctrlNum > MAX_CTRLS) ||
- (Controllers[ctrlNum] == (Controller *) 0)) {
- return (ScsiDevice *) NIL;
- }
- ctrlPtr = Controllers[ctrlNum];
- targetID = SCSI_TARGET_ID(devicePtr);
- lun = SCSI_LUN(devicePtr);
- MASTER_LOCK(&(ctrlPtr->mutex));
- /*
- * A device pointer of zero means that targetID/LUN
- * conflicts with that of the HBA. A NIL means the
- * device hasn't been attached yet.
- */
- if (ctrlPtr->devicePtr[targetID][lun] == (Device *) 0) {
- MASTER_UNLOCK(&(ctrlPtr->mutex));
- return (ScsiDevice *) NIL;
- }
- if (ctrlPtr->devicePtr[targetID][lun] != (Device *) NIL) {
- /*
- * Already attached once before. Use the cached value.
- */
- MASTER_UNLOCK(&(ctrlPtr->mutex));
- return (ScsiDevice *) (ctrlPtr->devicePtr[targetID][lun]);
- }
- /*
- * Allocate and fill in a ScsiDevice structure for the new device.
- */
- ctrlPtr->devicePtr[targetID][lun] = devPtr =
- (Device *) malloc(sizeof(Device));
- bzero((char *) devPtr, sizeof(Device));
- /*
- * Fill in the ScsiDevice structure that is returned to the caller.
- * It starts with a device queue for the device.
- */
- devPtr->handle.devQueue = Dev_QueueCreate(ctrlPtr->devQueues, 0,
- insertProc, (ClientData) devPtr);
- /*
- * Followed by a printable string sepcifying the devices location.
- */
- (void) sprintf(tmpBuffer, "%s#%d Target %d LUN %d", ctrlPtr->name, ctrlNum,
- devPtr->targetID, devPtr->handle.LUN);
- length = strlen(tmpBuffer);
- devPtr->handle.locationName = (char *) strcpy(malloc(length+1),tmpBuffer);
- /*
- * And the logical unit number. This is need because the LUN is formatted
- * into scsi control blocks by the higher level software.
- */
- devPtr->handle.LUN = lun;
- /*
- * The procedure of ours to call when releasing the device.
- */
- devPtr->handle.releaseProc = ReleaseProc;
- /*
- * The last part of the handle we need to fill in is the maximum size
- * transfer this HBA can support.
- */
- devPtr->handle.maxTransferSize = MAX_DMA_SIZE;
-
- devPtr->targetID = targetID;
- devPtr->ctrlPtr = ctrlPtr;
- MASTER_UNLOCK(&(ctrlPtr->mutex));
- return (ScsiDevice *) devPtr;
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * Dev<HBATYPE>Intr --
- *
- * Handle a SCSI command finished interrupt for the <HBATYPE> controller.
- *
- * Side effects:
- * Usually a process is notified that an I/O has completed.
- *
- *----------------------------------------------------------------------
- */
- Dev<HBATYPE>Intr(ctrlPtr)
- Controller *ctrlPtr; /* Controller interrupt was from. */
- {
- register CtrlRegs *regsPtr;
- Device *devPtr;
- unsigned char statusByte;
- List_Links *newRequestPtr;
- ClientData clientData;
-
- /*
- * Need to determine the device, request, statusByte and residual from
- * the command.
- */
- MASTER_LOCK(&(ctrlPtr->mutex));
- devPtr = ctrlPtr->scsiCmdPtr;
- scsiCmdPtr = ctrlPtr->scsiCmdPtr;
- residual = ctrlPtr->regs->residual;
- statusByte = ctrlPtr->regs->statusByte;
- /*
- * Use the request done procedure to notify the requestor.
- */
- RequestDone(devPtr, scsiCmdPtr, SUCCESS, statusByte,
- scsiCmdPtr->bufferLen - residual);
-
- /*
- * If the controller is not busy get another request from the queue and
- * start it.
- */
- if (!IS_CTRL_BUSY(ctrlPtr)) {
- newRequestPtr = Dev_QueueGetNextFromSet(ctrlPtr->devQueues,
- DEV_QUEUE_ANY_QUEUE_MASK,&clientData);
- if (newRequestPtr != (List_Links *) NIL) {
- entryAvailProc(clientData,newRequestPtr);
- }
- }
- MASTER_UNLOCK(&(ctrlPtr->mutex));
- return ;
-
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * entryAvailProc --
- *
- * Act upon an entry becomming available in the queue for this
- * controller. This routine is the Dev_Queue callback function that
- * is called whenever work becomes available for this controller.
- * If the controller is not already busy we start the
- * request.
- * NOTE: This routine is also called from DevSCSI3Intr to start the
- * next request after the previously one finishes.
- *
- * Results:
- * TRUE if we were able to process the requests. FALSE if the device
- * module should re-enqueue the request.
- *
- * Side effects:
- * Request may submitted to the device. Request callback
- * function may be called.
- *
- *----------------------------------------------------------------------
- */
-
- static Boolean
- entryAvailProc(clientData, newRequestPtr)
- ClientData clientData; /* Really the Device this request ready. */
- List_Links *newRequestPtr; /* The new SCSI request. */
- {
- register Device *devPtr = (Device *) clientData;
- register Controller *ctrlPtr = devPtr->ctrlPtr;
- register ScsiCmd *scsiCmdPtr = (ScsiCmd *) newRequestPtr;
- ReturnStatus status;
-
- /*
- * If we are busy (have an active request) just return. Otherwise
- * start the request.
- */
-
- if (IS_CTRL_BUSY(ctrlPtr)) {
- return FALSE;
- }
- /*
- * Call the function to send the SCSI command to the device.
- */
- status = StartCommand(devPtr, scsiCmdPtr);
- /*
- * If the command couldn't be started do the callback function.
- */
- if (status != SUCCESS) {
- RequestDone(devPtr,scsiCmdPtr,status,0,0);
- }
- return TRUE;
-
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * RequestDone --
- *
- * Process a request that has finished. Unless a SCSI check condition
- * bit is present in the status returned, the request call back
- * function is called. If check condition is set we fire off a
- * SCSI REQUEST SENSE to get the error sense bytes from the device.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The call back function may be called.
- *
- *----------------------------------------------------------------------
- */
-
- void
- RequestDone(devPtr,scsiCmdPtr,status,scsiStatusByte,amountTransferred)
- Device *devPtr; /* Device for request. */
- ScsiCmd *scsiCmdPtr; /* Request that finished. */
- ReturnStatus status; /* Status returned. */
- unsigned char scsiStatusByte; /* SCSI Status Byte. */
- int amountTransferred; /* Amount transferred by command. */
- {
- ReturnStatus senseStatus;
- Controller *ctrlPtr = devPtr->ctrlPtr;
-
-
- if (debug > 3) {
- printf("RequestDone for %s status 0x%x scsistatus 0x%x count %d\n",
- devPtr->handle.locationName, status,scsiStatusByte,
- amountTransferred);
- }
- /*
- * First check to see if this is the reponse of a HBA generated
- * REQUEST SENSE command. If this is the case, we can process
- * the callback of the frozen command for this device and
- * allow the flow of command to the device to be resummed.
- */
- if (scsiCmdPtr->doneProc == SpecialSenseProc) {
- MASTER_UNLOCK(&(ctrlPtr->mutex));
- (devPtr->frozen.scsiCmdPtr->doneProc)(devPtr->frozen.scsiCmdPtr,
- SUCCESS,
- devPtr->frozen.statusByte,
- devPtr->frozen.amountTransferred,
- amountTransferred,
- devPtr->senseBuffer);
- MASTER_LOCK(&(ctrlPtr->mutex));
- SET_CTRL_FREE(ctrlPtr);
- return;
- }
- /*
- * This must be a outside request finishing. If the request
- * suffered an error or the HBA or the scsi status byte
- * says there is no error sense present, we can do the
- * callback and free the controller.
- */
- if ((status != SUCCESS) || !SCSI_CHECK_STATUS(scsiStatusByte)) {
- MASTER_UNLOCK(&(ctrlPtr->mutex));
- (scsiCmdPtr->doneProc)(scsiCmdPtr, status, scsiStatusByte,
- amountTransferred, 0, (char *) 0);
- MASTER_LOCK(&(ctrlPtr->mutex));
- SET_CTRL_FREE(ctrlPtr);
- return;
- }
- /*
- * If we got here than the SCSI command came back from the device
- * with the CHECK bit set in the status byte.
- * Need to perform a REQUEST SENSE. Move the current request
- * into the frozen state and issue a REQUEST SENSE.
- */
- devPtr->frozen.scsiCmdPtr = scsiCmdPtr;
- devPtr->frozen.statusByte = scsiStatusByte;
- devPtr->frozen.amountTransferred = amountTransferred;
- /*
- * Use the routine DevScsiSenseCmd to quick format a REQUEST_SENSE
- * command for the device.
- */
- DevScsiSenseCmd((ScsiDevice *)devPtr, DEV_MAX_SENSE_BYTES,
- devPtr->senseBuffer, &(devPtr->SenseCmd));
- /*
- * Use a special procedure so we can tell we one of our REQUEST_SENSE
- * finishes.
- */
- devPtr->SenseCmd.doneProc = SpecialSenseProc,
- senseStatus = StartCommand(devPtr, &(devPtr->SenseCmd));
- /*
- * If we got an HBA error on the REQUEST SENSE we end the outside
- * command with the SUCCESS status but zero sense bytes returned.
- */
- if (senseStatus != SUCCESS) {
- MASTER_UNLOCK(&(ctrlPtr->mutex));
- (scsiCmdPtr->doneProc)(scsiCmdPtr, status, scsiStatusByte,
- amountTransferred, 0, (char *) 0);
- MASTER_LOCK(&(ctrlPtr->mutex));
- SET_CTRL_FREE(ctrlPtr);
- }
-
- }
-
-